#ifndef JNCTYPECONV_H
#define JNCTYPECONV_H

#include <boost/any.hpp>
#include <boost/preprocessor/seq/elem.hpp>
#include <boost/preprocessor/seq.hpp>
#include <boost/preprocessor/repetition.hpp>
#include <boost/preprocessor/arithmetic/sub.hpp>
#include <boost/preprocessor/punctuation/comma_if.hpp>
#include <boost/preprocessor/punctuation/comma.hpp>
#include <boost/function.hpp>
#include <boost/call_traits.hpp>
#include <boost/preprocessor/stringize.hpp>
#include "jncDefs.h"

// Conversion classes, cast XML objects back to native types

#define OUTARGLIST(z, n, data) BOOST_PP_COMMA_IF(n) BOOST_PP_CAT(data, n)
#define OUTTYPELIST(z, n, data) BOOST_PP_COMMA() class BOOST_PP_CAT(data, n)

#define EXPANDTYPE(n, t)			\
  BOOST_PP_REPEAT(n, OUTTYPELIST, t)
#define EXPANDARG(n, t)				\
  BOOST_PP_REPEAT(n, OUTARGLIST, t)
#define CONV_MEMFN(n, data)						\
  template<class Result, class Type EXPANDTYPE(n, T)>			\
    struct data {							\
    typedef Result (Type::*mfnConv)(EXPANDARG(n, T));			\
    typename boost::call_traits<mfnConv>::reference operator()(jncTypev& v) \
    {									\
      mfnConv* tmp;							\
      try {								\
	return *(boost::any_cast<mfnConv*>(v));				\
      }									\
      catch(boost::bad_any_cast& e) {					\
	std::string msg("Conversion from ");				\
	msg += (std::string(v.type().name()) + " to ") + typeid(tmp).name(); \
	throw RnException(ECONV, msg.c_str(), e);			\
      }									\
    }									\
  };

// Conversion fn for basic and pointer Types
template<class T>
T& castTo(jncTypev& v) {
  T* tmp;
  try {
    return *(boost::any_cast<T*>(v));
  }
  catch(boost::bad_any_cast& e) {
    std::string msg("Conversion from ");				
    msg += (std::string(v.type().name()) + " to ") + typeid(tmp).name(); 
    throw RnException(ECONV, msg.c_str(), e);				
  }
}
// Conversion fn for array Type
template<class T>
T& castArray(jncTypev& v) {
  T** tmp;
  try {
    return *(boost::any_cast<T>(&v));
  }
  catch(boost::bad_any_cast& e) {
    std::string msg("Conversion from ");				
    msg += (std::string(v.type().name()) + " to ") + typeid(tmp).name(); 
    throw RnException(ECONV, msg.c_str(), e);				
  }
}

// member Fn Conversion
#define DEF_MEMFN(z, n, data)					\
  CONV_MEMFN(n, BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_ELEM(n, data)))

// Conversion fn for primitive types
#define DCL_CONVT(z, n, data)						\
  BOOST_PP_EXPR_IF(BOOST_PP_SUB(BOOST_PP_SEQ_SIZE(BOOST_PP_SEQ_ELEM(n, data)), 1), SELF BOOST_PP_SEQ_TAIL(BOOST_PP_SEQ_ELEM(n, data))& BOOST_PP_SEQ_HEAD(BOOST_PP_SEQ_ELEM(n, data))(jncTypev& v);)


BOOST_PP_REPEAT(JINC_MAX_ARG, DEF_MEMFN, LOCAL)
  
template<class T>
T& jcmp(jncTypev& v) {
  return castTo<T>(v);
}
     BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(BLOCAL), DCL_CONVT, BLOCAL)
  BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(PLOCAL), DCL_CONVT, PLOCAL)
  BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(ALOCAL), DCL_CONVT, ALOCAL)


#define STREXPR(r, data, x) (BOOST_PP_STRINGIZE(x))
#define REFEXPR(r, data, x) (>x)

  int numberConv(jncTypev&);

#endif
